using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace Chaotic.DataBase.DataAccess
{
	public class DataAccessFactory
	{ 

		private static readonly Dictionary<string, Assembly> AssDic = new();

		public static DataAccessFactory Instance => new();

        private DataAccessFactory()
		{
		}

		public IDataAccess? CreateDataAccess()
		{
			return GetAssemblyDataAccess(PersistenceProperty.DatabaseType, PersistenceProperty.ConnectionString);
		}

		private IDataAccess? GetAssemblyDataAccess(DatabaseType type, string connectStr)
		{
			return GetDataAccess(type, connectStr);
		}

		private IDataAccess? GetDataAccess(DatabaseType type, string connectStr)
		{
            //string text = string.Empty;
            //string className = string.Empty;
            //switch (type)
            //{
            //	case DatabaseType.MSSQLServer:
            //		text = "Chaotic.DataBase.MSSQLAccess";
            //		className = "Chaotic.DataBase.MSSQLAccess.MsSqlDataAccess";
            //		break;
            //	case DatabaseType.Oracle:
            //		text = "Chaotic.DataBase.OracleAccess";
            //		className = "Chaotic.DataBase.OracleAccess.OracleDataAccessClient";
            //		break;
            //	case DatabaseType.DB2:
            //		text = "Chaotic.DataBase.DBAccessLibrary";
            //		className = "Chaotic.DataBase.DBAccessLibrary.DB2DataAccess";
            //		break;
            //	case DatabaseType.OleDBSupported:
            //		text = "Chaotic.DataBase.OLEDBAccess";
            //		className = "Chaotic.DataBase.OLEDBAccess.OledbDataAccess";
            //		break;
            //	case DatabaseType.MongoDB:
            //		text = "SkynetMongoDBAccess";
            //		className = "SkynetMongoDBAccess.MongoAccess";
            //		break;
            //	case DatabaseType.ODBC:
            //		text = "Chaotic.DataBase.ODBCAccess";
            //		className = "Chaotic.DataBase.ODBCAccess.OdbcDataAccess";
            //		break;
            //             case DatabaseType.Mysql:
            //                 text = "Chaotic.DataBase.MySQLAccess";
            //                 className = "Chaotic.DataBase.MySQLAccess.MysqlDataAccess";
            //                 break;
            //             case DatabaseType.Sqlite:
            //                 text = "Chaotic.DataBase.SqliteAccess";
            //                 className = "Chaotic.DataBase.SqliteAccess.SqliteDataAccess";
            //                 break;
            //         }
            //Assembly assembly = GetAssembly(text);

            Assembly assembly = GetAssemblyPath(type); 
            try
			{
                IEnumerable<Type> dataAccessInstance = GetClassesImplementingInterface<IDataAccess>(assembly);
                IEnumerable<Type> dbTypeInstance = GetClassesImplementingInterface<IDbTypeEnum>(assembly);

                if (!PersistenceProperty.GetDbTypeEunm.ContainsKey(type) && dbTypeInstance.Any())
				{
					Type dbtype = dbTypeInstance.First();

                    IDbTypeEnum value = dbtype.CreateInstance<IDbTypeEnum>();
					PersistenceProperty.GetDbTypeEunm.Add(type, value);
				}
				if (dataAccessInstance.Any())
				{
					Type accessType = dataAccessInstance.First();
                    return accessType.CreateInstance<IDataAccess>([typeof(string)], [connectStr]);
				}
                throw new Exception("指定数据库所需实现的IDataAccess接口不存在，请查看DLL是否正确：" + assembly.FullName); 
            }
			catch (Exception ex)
			{
				string message = ex.Message;
				if (ex.InnerException != null)
				{
					message = ex.InnerException.Message;
				}
				throw new Exception("数据库连接字符串错误，请查看是否缺少必要的文件。Error:" + message);
			}
		}

		private Assembly GetAssembly(string path)
		{
			string text = string.Empty;
			Assembly assembly;
			try
			{
				text = AppDomain.CurrentDomain.BaseDirectory + "\\" + path + ".dll";
				if (!File.Exists(text))
				{
					text = Directory.GetCurrentDirectory() + "\\" + path + ".dll";
				}
				if (AssDic.ContainsKey(text))
				{
					assembly = AssDic[text];
				}
				else
				{
                
                    assembly = Assembly.LoadFile(text);
					AssDic[text] = assembly; 

                }
			}
			catch
			{
				throw new Exception("指定数据库所需加载的DLL不存在，请手动将DLL放置到路径：" + text);
			}
			return assembly;
		}

   
		public Assembly GetAssemblyPath(DatabaseType type)
		{
			string text = string.Empty;
			switch (type)
			{
				case DatabaseType.MSSQLServer:
					text = "Chaotic.DataBase.MSSQLAccess";
					break;
				case DatabaseType.Oracle:
					text = "Chaotic.DataBase.OracleAccess";
					break;
				case DatabaseType.DB2:
					text = "Chaotic.DataBase.DBAccessLibrary";
					break;
				case DatabaseType.OleDBSupported:
					text = "Chaotic.DataBase.OLEDBAccess";
					break;
				case DatabaseType.MongoDB:
					text = "SkynetMongoDBAccess";
					break;
				case DatabaseType.ODBC:
					text = "Chaotic.DataBase.ODBCAccess";
					break;
				case DatabaseType.Mysql:
					text = "Chaotic.DataBase.MySQLAccess";
					break;
				case DatabaseType.Sqlite:
					text = "Chaotic.DataBase.SqliteAccess";
					break;
			}

            return GetAssembly(text); 
        }

        /// <summary>
        /// 获取程序集中实现指定接口的所有类
        /// </summary>
        public IEnumerable<Type> GetClassesImplementingInterface<TInterface>(Assembly assembly)
        {
            return assembly.GetTypes()
                .Where(t => t.IsClass && !t.IsAbstract && typeof(TInterface).IsAssignableFrom(t));
        }
    }
}